import fs from 'fs/promises';
import path from 'path';
import { createHash } from 'crypto';

/**
 * 支持的图片文件扩展名
 */
const SUPPORTED_EXTENSIONS = new Set([
  '.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.tiff', '.tif',
  '.heic', '.heif', '.raw', '.cr2', '.nef', '.arw', '.dng', '.orf'
]);

/**
 * 支持的图片 MIME 类型
 */
const SUPPORTED_MIME_TYPES = new Set([
  'image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/webp',
  'image/tiff', 'image/heic', 'image/heif', 'image/x-canon-cr2',
  'image/x-nikon-nef', 'image/x-sony-arw', 'image/x-adobe-dng',
  'image/x-olympus-orf'
]);

/**
 * 扫描结果接口
 */
export interface ScanResult {
  files: FileInfo[];
  totalFiles: number;
  totalSize: number;
  errors: ScanError[];
}

/**
 * 文件信息接口
 */
export interface FileInfo {
  filepath: string;
  filename: string;
  filesize: number;
  mimetype: string;
  extension: string;
  createdAt: Date;
  modifiedAt: Date;
  hash?: string;
}

/**
 * 扫描错误接口
 */
export interface ScanError {
  filepath: string;
  error: string;
  code?: string;
}

/**
 * 扫描选项接口
 */
export interface ScanOptions {
  recursive?: boolean;          // 是否递归扫描子目录
  includeHidden?: boolean;      // 是否包含隐藏文件
  generateHash?: boolean;       // 是否生成文件哈希
  maxDepth?: number;           // 最大扫描深度
  followSymlinks?: boolean;     // 是否跟随符号链接
  onProgress?: (progress: ScanProgress) => void; // 进度回调
}

/**
 * 扫描进度接口
 */
export interface ScanProgress {
  currentFile: string;
  processedFiles: number;
  totalFiles: number;
  currentSize: number;
  totalSize: number;
  errors: number;
}

/**
 * 文件扫描器类
 */
export class FileScanner {
  private aborted = false;
  private currentDepth = 0;

  /**
   * 扫描指定目录
   */
  public async scanDirectory(
    directoryPath: string, 
    options: ScanOptions = {}
  ): Promise<ScanResult> {
    const {
      recursive = true,
      includeHidden = false,
      generateHash = true,
      maxDepth = 10,
      followSymlinks = false,
      onProgress
    } = options;

    console.log('开始扫描目录:', directoryPath);
    
    this.aborted = false;
    this.currentDepth = 0;

    const result: ScanResult = {
      files: [],
      totalFiles: 0,
      totalSize: 0,
      errors: []
    };

    try {
      // 检查目录是否存在
      await this.checkDirectoryExists(directoryPath);

      // 递归扫描目录
      await this.scanDirectoryRecursive(
        directoryPath,
        result,
        {
          recursive,
          includeHidden,
          generateHash,
          maxDepth,
          followSymlinks,
          onProgress: onProgress || (() => {})
        }
      );

      console.log(`扫描完成: 找到 ${result.files.length} 个图片文件`);
      
    } catch (error) {
      console.error('扫描目录失败:', error);
      result.errors.push({
        filepath: directoryPath,
        error: error instanceof Error ? error.message : String(error),
        code: error instanceof Error && 'code' in error ? String(error.code) : undefined
      });
    }

    return result;
  }

  /**
   * 递归扫描目录
   */
  private async scanDirectoryRecursive(
    directoryPath: string,
    result: ScanResult,
    options: Required<ScanOptions>
  ): Promise<void> {
    if (this.aborted) return;
    
    // 检查扫描深度
    if (this.currentDepth >= options.maxDepth) {
      console.warn(`达到最大扫描深度 ${options.maxDepth}，跳过: ${directoryPath}`);
      return;
    }

    try {
      const entries = await fs.readdir(directoryPath, { withFileTypes: true });
      
      for (const entry of entries) {
        if (this.aborted) break;

        const fullPath = path.join(directoryPath, entry.name);

        // 跳过隐藏文件（如果不包含隐藏文件）
        if (!options.includeHidden && entry.name.startsWith('.')) {
          continue;
        }

        try {
          if (entry.isDirectory()) {
            // 递归扫描子目录
            if (options.recursive) {
              this.currentDepth++;
              await this.scanDirectoryRecursive(fullPath, result, options);
              this.currentDepth--;
            }
          } else if (entry.isFile() || (entry.isSymbolicLink() && options.followSymlinks)) {
            // 处理文件
            await this.processFile(fullPath, result, options);
          }
        } catch (error) {
          console.error(`处理文件失败: ${fullPath}`, error);
          result.errors.push({
            filepath: fullPath,
            error: error instanceof Error ? error.message : String(error),
            code: error instanceof Error && 'code' in error ? String(error.code) : undefined
          });
        }
      }
    } catch (error) {
      console.error(`读取目录失败: ${directoryPath}`, error);
      result.errors.push({
        filepath: directoryPath,
        error: error instanceof Error ? error.message : String(error),
        code: error instanceof Error && 'code' in error ? String(error.code) : undefined
      });
    }
  }

  /**
   * 处理单个文件
   */
  private async processFile(
    filePath: string,
    result: ScanResult,
    options: Required<ScanOptions>
  ): Promise<void> {
    try {
      // 获取文件统计信息
      const stats = await fs.stat(filePath);
      
      // 跳过非常规文件
      if (!stats.isFile()) {
        return;
      }

      // 检查文件扩展名
      const extension = path.extname(filePath).toLowerCase();
      if (!this.isSupportedImageFile(extension)) {
        return;
      }

      // 创建文件信息对象
      const fileInfo: FileInfo = {
        filepath: filePath,
        filename: path.basename(filePath),
        filesize: stats.size,
        mimetype: this.getMimeType(extension),
        extension,
        createdAt: stats.birthtime,
        modifiedAt: stats.mtime
      };

      // 生成文件哈希（如果需要）
      if (options.generateHash) {
        fileInfo.hash = await this.generateFileHash(filePath);
      }

      // 添加到结果中
      result.files.push(fileInfo);
      result.totalFiles++;
      result.totalSize += stats.size;

      // 调用进度回调
      if (options.onProgress) {
        options.onProgress({
          currentFile: filePath,
          processedFiles: result.files.length,
          totalFiles: result.totalFiles,
          currentSize: stats.size,
          totalSize: result.totalSize,
          errors: result.errors.length
        });
      }

    } catch (error) {
      console.error(`处理文件失败: ${filePath}`, error);
      result.errors.push({
        filepath: filePath,
        error: error instanceof Error ? error.message : String(error),
        code: error instanceof Error && 'code' in error ? String(error.code) : undefined
      });
    }
  }

  /**
   * 检查目录是否存在
   */
  private async checkDirectoryExists(directoryPath: string): Promise<void> {
    try {
      const stats = await fs.stat(directoryPath);
      if (!stats.isDirectory()) {
        throw new Error(`路径不是目录: ${directoryPath}`);
      }
    } catch (error) {
      if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
        throw new Error(`目录不存在: ${directoryPath}`);
      }
      throw error;
    }
  }

  /**
   * 检查是否为支持的图片文件
   */
  private isSupportedImageFile(extension: string): boolean {
    return SUPPORTED_EXTENSIONS.has(extension.toLowerCase());
  }

  /**
   * 根据扩展名获取 MIME 类型
   */
  private getMimeType(extension: string): string {
    const mimeMap: Record<string, string> = {
      '.jpg': 'image/jpeg',
      '.jpeg': 'image/jpeg',
      '.png': 'image/png',
      '.gif': 'image/gif',
      '.bmp': 'image/bmp',
      '.webp': 'image/webp',
      '.tiff': 'image/tiff',
      '.tif': 'image/tiff',
      '.heic': 'image/heic',
      '.heif': 'image/heif',
      '.raw': 'image/x-canon-crw',
      '.cr2': 'image/x-canon-cr2',
      '.nef': 'image/x-nikon-nef',
      '.arw': 'image/x-sony-arw',
      '.dng': 'image/x-adobe-dng',
      '.orf': 'image/x-olympus-orf'
    };

    return mimeMap[extension.toLowerCase()] || 'application/octet-stream';
  }

  /**
   * 生成文件哈希值
   */
  private async generateFileHash(filePath: string): Promise<string> {
    try {
      const fileBuffer = await fs.readFile(filePath);
      const hash = createHash('sha256');
      hash.update(fileBuffer);
      return hash.digest('hex');
    } catch (error) {
      console.error(`生成文件哈希失败: ${filePath}`, error);
      throw error;
    }
  }

  /**
   * 中止扫描
   */
  public abort(): void {
    console.log('正在中止文件扫描...');
    this.aborted = true;
  }

  /**
   * 检查是否已中止
   */
  public isAborted(): boolean {
    return this.aborted;
  }

  /**
   * 获取支持的文件扩展名列表
   */
  public static getSupportedExtensions(): string[] {
    return Array.from(SUPPORTED_EXTENSIONS);
  }

  /**
   * 获取支持的 MIME 类型列表
   */
  public static getSupportedMimeTypes(): string[] {
    return Array.from(SUPPORTED_MIME_TYPES);
  }

  /**
   * 检查文件是否为支持的图片格式
   */
  public static isSupportedFile(filePath: string): boolean {
    const extension = path.extname(filePath).toLowerCase();
    return SUPPORTED_EXTENSIONS.has(extension);
  }

  /**
   * 格式化文件大小
   */
  public static formatFileSize(bytes: number): string {
    const units = ['B', 'KB', 'MB', 'GB', 'TB'];
    let size = bytes;
    let unitIndex = 0;

    while (size >= 1024 && unitIndex < units.length - 1) {
      size /= 1024;
      unitIndex++;
    }

    return `${size.toFixed(unitIndex === 0 ? 0 : 1)} ${units[unitIndex]}`;
  }
}